package net.blank.effectClass
{
	import flash.events.EventDispatcher;
	
	import net.blank.timerUtl.PublicTimer;
	
	/**特效开始执行*/
	[Event(name="effect_start", type="EffectEvent")]
	
	/**特效执行结束*/
	[Event(name="effect_end", type="EffectEvent")]
	
	/**补间开始执行*/
	[Event(name="tween_start", type="EffectEvent")]
	
	/**补间执行结束*/
	[Event(name="tween_end", type="EffectEvent")]
	
	/**
	 * 基于 PublicTimer 的特效类的基类。<br/>
	 * 在此对象生命周期内请确保对此对象的引用，否则AS3的自动回收机制可能将此对象作为垃圾进行回收，使其特效的执行不能完成。
	 * 即，把此对象声明为类的实例变量，而不声明在函数体内。<br/>
	 * <b>AS3的Tween类也存此问题</b>。<br/>
	 * 如果要指定文本对象(TextField、UITextField...)的alpha值（半透明效果），必须设置文本对象的 blendMode=BlendMode.layer 。
	 * @author blank
	 */
	public class EffectForTimer extends EventDispatcher
	{
		/**
		 * 计时器(执行间隔500ms)
		 */
		public static const TIMER_25MS:PublicTimer=new PublicTimer(25);
		
		/**特效的目标。*/
		protected var _target:*;
		
		/**对帧数进行累加，以确定当前执行到了第几帧。*/
		private var $iFrame:uint;
		
		/**特效执行的总帧数，主要用于缓函数。*/
		private var $totalFrame:uint;
		
		/**变化的初始值。*/
		private var _initValue:Number;
		
		/**变化的终止值。*/
		private var _endValue:Number;
		
		/**延迟执行的帧数。*/
		private var _startDelayFrame:uint;
		
		/**特效实际执行的帧数*/
		protected var _runTotalFrame:uint;
		
		/**每执行一步所要变化的值。*/
		private var $stepChangeValue:Number;
		
		/**总共所需要变化的值。*/
		private var $totalChangeValue:Number;
		
		/**执行属性变化的函数。*/
		private var _execFunc:Function;
		
		/**缓动函数。*/
		private var _easingFunc:Function;
		
		/**临时记录要改变的属性值（$stepChangeValue）。*/
		private var $tempValue:Number;
		
		/**是否正在执行*/
		private var _running:Boolean;
		
		//functions and methods----------------------------------------
		/**执行特效的ENTER_FRAME。*/
		private function onTimer():Boolean{
			if($iFrame >= _startDelayFrame){
				
				if($iFrame == _startDelayFrame){
					dispatchEvent(new EffectEvent(EffectEvent.TWEEN_START));
				}
				
				if(_easingFunc == null){
					$tempValue += $stepChangeValue
				}else{
					/* 缓动函数参数。
					t，时间。
					b，初始位置。
					c，总共要移动的位置。
					d，总共要执行的时间。参考以下内容：
					param t Specifies time.
					param b Specifies the initial position of a component.
					param c Specifies the total change in position of the component.
					param d Specifies the duration of the effect, in milliseconds.
					*/
					$tempValue=_easingFunc(
						$iFrame-_startDelayFrame,
						_initValue,
						$totalChangeValue,
						_runTotalFrame
					);
				}
				
				if($iFrame == $totalFrame){
					_execFunc(endValue,_target);
					
					dispatchEvent(new EffectEvent(EffectEvent.TWEEN_END));
					stop();
				}else{
					_execFunc($tempValue,_target);
				}
			}
			
			$iFrame++;
			
			return true;
		}
		
		/**结束执行特效。*/
		public function stop():void{
			TIMER_25MS.removeFunc(onTimer);
			
			_running=false;
			
			dispatchEvent(new EffectEvent(EffectEvent.EFFECT_END));
		}
		
		/**
		 * 开始执行特效。
		 * @param target 特效的目标。<br/>
		 * 所以请保证此处指定的对象不会在此对象的生命周期还未结束时被释放掉。<br/>
		 * 在此处指定 target 将会覆盖掉构造函数中对 target 值的指定，同时也会覆盖 target 属性指定的值。<br/>
		 * <b>一般指定为要执行特效的对象。</b>
		 */
		public function start(target:*=null):void{
			if(target){
				_target = target;
			}
			
			if(_target && _execFunc != null){
				if(_initValue != _endValue){
					if(_runTotalFrame > 0){
						$totalChangeValue=_endValue-_initValue;
						
						if(_easingFunc == null){
							$tempValue=_initValue;
							$stepChangeValue = $totalChangeValue / _runTotalFrame;
						}
						
						$totalFrame = _runTotalFrame + _startDelayFrame;
						
						$iFrame=1;
						$tempValue=_initValue;
						
						_running=true;
						
						dispatchEvent(new EffectEvent(EffectEvent.EFFECT_START));
						
						_execFunc(_initValue,_target);
						
						TIMER_25MS.addFunc(onTimer);
					}
				}
			}
		}
		
		/**翻转执行，即初始值变为终止值，终止值变为初始值。*/
		public function Reversed():void{
			var temp:Number=_initValue;
			_initValue=_endValue;
			_endValue=temp;
		}
		
		/**摧毁此对象。*/
		public function destroy():void{
			stop();
			
			_target=null;
			_execFunc=null;
			_easingFunc=null;
		}
		
		/**
		 * 基于 PublicTimer 的特效类的基类。
		 * @pararm target 特效的目标。<br/>
		 * 如果指定了target属性，则此处指定的target值将被覆盖；<br/>
		 * 如果在play方法传了target参数，则target属性的值也将被覆盖。<br/>
		 * <b>一般指定为要执行特效的对象。</b>
		 */
		public function EffectForTimer(target:*=null)
		{
			_target=target;
		}
		
		//getter and setter------------------------------------------------
		/**
		 * 特效的目标。<br/>
		 * 请保证此处指定的对象不会在此对象的生命周期还未结束时被释放掉。<br/>
		 * 指定此属性会覆盖被构造函数中对 target 值的指定。<br/>
		 * 如果在 start 方法中指定 target 值，则此属性也将被覆盖。<br/>
		 * <b>一般指定为要执行特效的对象。</b>
		 */
		public function set target(value:*):void{
			if(value != null){
				_target=value;
			}
		}
		public function get target():*{
			return _target;
		}
		
		/**变化的初始值。*/
		public function set initValue(value:Number):void{
			if(_initValue != value){
				_initValue=value;
			}
		}
		public function get initValue():Number{
			return _initValue;
		}
		
		/**变化的终止值。*/
		public function set endValue(value:Number):void{
			if(_endValue != value){
				_endValue=value;
			}
		}
		public function get endValue():Number{
			return _endValue;
		}
		
		/**延迟执行的帧数。*/
		public function set startDelayFrame(value:uint):void{
			if(_startDelayFrame != value){
				_startDelayFrame=value;
			}
		}
		public function get startDelayFrame():uint{
			return _startDelayFrame;
		}
		
		/**特效实际执行的帧数*/
		public function set runTotalFrame(value:uint):void{
			if(_runTotalFrame != value){
				_runTotalFrame=value;
			}
		}
		public function get runTotalFrame():uint{
			return _runTotalFrame;
		}
		
		/* &nbsp; HTML中的空格。*/
		/**
		 * 执行属性变化的函数。函数必须有两个参数，多于2个则多余部分必须为可选参数。例子如下：<br/>
		 * <table><tr><td>
		 * public function changeValue(value:Number, target:IEventDispatcher){<br/>
		 * &nbsp;&nbsp;&nbsp;&nbsp;var tempObject:Object=target as Object;<br/>
		 * &nbsp;&nbsp;&nbsp;&nbsp;tempObject.alpha=value;<br/>
		 * }
		 * </td></tr></table>
		 */
		public function set execFunction(value:Function):void{
			if(value != _execFunc){
				_execFunc=value;
			}
		}
		public function get execFunction():Function{
			return _execFunc;
		}
		
		/**
		 * 缓动函数,函数至少有四个参数，多于4个则多余部分必须为可选参数。例子如下：<br/>
		 * <table><tr><td>
		 * public function easingFunc(t:Number, b:Number, c:Number, d:Number):void{<br/>
		 * &nbsp;&nbsp;&nbsp;&nbsp;return -c &#42; Math.cos(t &#47; d &#42; (Math.PI &#47; 2)) + c + b;<br/>
		 * }
		 * </td></tr></table>
		 * 一般情况下，这四个参数（t,b,c,d）分别表示：<br/>
		 * <table>
		 * <caption><td>参数</td><td>说明</td></caption>
		 * <tr><td>t</td><td>时间（time），此类中为调用 start 方法后的当前帧号。</td></tr>
		 * <tr><td>b</td><td>开始值（begin），此类中为 initValue 。</td></tr>
		 * <tr><td>c</td><td>统计（count），此类中为 endValue - initValue ，即总共要变化的数目。</td></tr>
		 * <tr><td>d</td><td>持续运行的时间（duration），此类中为 runTotalFrame ，即总共要执行的帧数。</td></tr>
		 * </table>
		 * <b>具体可参考flex下 mx.effects.easing.Back 。</b>
		 */
		public function set easingFunction(value:Function):void{
			if(_easingFunc != value){
				_easingFunc=value;
			}
		}
		public function get easingFunction():Function{
			return _easingFunc;
		}
		
		/**是否正在执行*/
		public function get running():Boolean{
			return _running;
		}
	}
}